import React, { useState, useEffect, useRef } from 'react';
const LogoSVG = ({ type }) => {
const logos = {
okta: (
),
slack: (
),
google: (
),
aws: (
),
github: (
),
workday: (
),
user: (
),
greenhouse: (
),
jamf: (
),
ninjaone: (
),
zapier: (
),
datadog: (
),
pagerduty: (
),
jira: (
),
confluence: (
),
onepassword: (
),
autopkg: (
),
chocolatey: (
),
vmware: (
),
gmail: (
),
drive: (
),
default: (
)
};
return logos[type] || logos.default;
};
const logos = {
okta: 'https://cdn.worldvectorlogo.com/logos/okta-2.svg',
slack: 'https://cdn.worldvectorlogo.com/logos/slack-new-logo.svg',
google: 'https://cdn.worldvectorlogo.com/logos/google-icon.svg',
jamf: 'https://cdn.worldvectorlogo.com/logos/jamf.svg',
workday: 'https://cdn.worldvectorlogo.com/logos/workday-logo-2020.svg',
jira: 'https://cdn.worldvectorlogo.com/logos/jira-1.svg',
confluence: 'https://cdn.worldvectorlogo.com/logos/confluence-1.svg',
datadog: 'https://cdn.worldvectorlogo.com/logos/datadog.svg',
pagerduty: 'https://cdn.worldvectorlogo.com/logos/pagerduty.svg',
aws: 'https://cdn.worldvectorlogo.com/logos/aws-2.svg',
zapier: 'https://cdn.worldvectorlogo.com/logos/zapier.svg',
greenhouse: 'https://cdn.worldvectorlogo.com/logos/greenhouse-software.svg',
ninjaone: 'https://cdn.worldvectorlogo.com/logos/ninjarmm.svg',
gmail: 'https://cdn.worldvectorlogo.com/logos/gmail-icon.svg',
drive: 'https://cdn.worldvectorlogo.com/logos/google-drive.svg',
autopkg: 'https://cdn.worldvectorlogo.com/logos/apple.svg',
chocolatey: 'https://cdn.worldvectorlogo.com/logos/chocolatey.svg',
onepassword: 'https://cdn.worldvectorlogo.com/logos/1password.svg',
user: 'https://cdn.worldvectorlogo.com/logos/user.svg',
github: 'https://cdn.worldvectorlogo.com/logos/github-icon-1.svg',
vmware: 'https://cdn.worldvectorlogo.com/logos/vmware.svg',
};
const Node = ({ x, y, label, scale = 1, logo, isUser, isActive, isDimmed, onDragStart, isDragging, animating, stepNumber, onInfoClick }) => {
const [currentX, setCurrentX] = useState(x);
const [currentY, setCurrentY] = useState(y);
useEffect(() => {
if (!animating) {
setCurrentX(x);
setCurrentY(y);
return;
}
const startX = currentX;
const startY = currentY;
const deltaX = x - startX;
const deltaY = y - startY;
const duration = 800;
const startTime = Date.now();
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
setCurrentX(startX + deltaX * eased);
setCurrentY(startY + deltaY * eased);
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
}, [x, y, animating]);
const handleInfoClick = (e) => {
e.stopPropagation();
if (onInfoClick) {
onInfoClick();
}
};
return (
{stepNumber && (
{stepNumber}
)}
{onInfoClick && (
i
)}
{logo && (

)}
{label}
);
};
const EdgeLabel = ({ x, y, label, sublabel, color, onDragStart, isDragging }) => (
{label}
{sublabel && (
{sublabel}
)}
);
const ActionBadge = ({ x, y, label, sublabel, color }) => (
{label}
{sublabel && (
{sublabel}
)}
);
const CategoryLabel = ({ x, y, label }) => (
{label}
);
const AnimatedEdge = ({ x1, y1, x2, y2, color = '#3b82f6', controlPoints }) => {
const [offset, setOffset] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setOffset(prev => (prev + 2) % 20);
}, 50);
return () => clearInterval(interval);
}, []);
let pathD;
if (controlPoints && controlPoints.length > 0) {
pathD = `M ${x1} ${y1}`;
if (controlPoints.length === 1) {
pathD += ` Q ${controlPoints[0].x} ${controlPoints[0].y}, ${x2} ${y2}`;
} else if (controlPoints.length === 2) {
pathD += ` C ${controlPoints[0].x} ${controlPoints[0].y}, ${controlPoints[1].x} ${controlPoints[1].y}, ${x2} ${y2}`;
}
} else {
pathD = `M ${x1} ${y1} L ${x2} ${y2}`;
}
return (
<>
>
);
};
const DotGrid = () => (
);
const DotGridBackground = () => (
);
const ITInfrastructureDiagram = () => {
const [zoomLevel, setZoomLevel] = useState('30000ft');
const [activeFlow, setActiveFlow] = useState(null);
const [pan, setPan] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [isDragging, setIsDragging] = useState(false);
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
const [draggingNode, setDraggingNode] = useState(null);
const [draggingLabel, setDraggingLabel] = useState(null);
const [nodePositions, setNodePositions] = useState({});
const [labelPositions, setLabelPositions] = useState({});
const [animatingNodes, setAnimatingNodes] = useState(false);
const [visibleEdges, setVisibleEdges] = useState([]);
const [canvasSize, setCanvasSize] = useState({ width: 1800, height: 800 });
const [selectedNode, setSelectedNode] = useState(null);
const svgRef = useRef(null);
const nodeInfo = {
okta: {
'30000ft': { title: 'Okta - Identity Provider', description: 'Centralized identity and access management platform.' },
'3000ft': { title: 'Okta - IDP (Identity Provider)', description: 'Single sign-on (SSO) platform that manages user authentication and authorization across all company applications. Provisions users via SCIM protocol.' },
'30ft': { title: 'Okta - Identity Provider', description: 'Enterprise IDP managing SSO, MFA, and lifecycle management. Integrates via SCIM 2.0 API for user provisioning. Supports SAML 2.0, OAuth 2.0, and OpenID Connect protocols. Central hub for all application access with 500+ pre-built integrations.' }
},
workday: {
'30000ft': { title: 'Workday - HR System', description: 'Human resources and workforce management platform.' },
'3000ft': { title: 'Workday - HRIS', description: 'Enterprise HR Information System managing employee data, payroll, benefits, and organizational structure. Source of truth for employee records.' },
'30ft': { title: 'Workday - Human Capital Management', description: 'Cloud-based HRIS handling full employee lifecycle from hire to retire. Integrates with Okta and Google via REST API for automated provisioning. Manages compensation, performance reviews, time tracking, and organizational hierarchy. Triggers downstream provisioning through webhooks.' }
},
greenhouse: {
'30000ft': { title: 'Greenhouse - Recruiting', description: 'Applicant tracking and recruiting platform.' },
'3000ft': { title: 'Greenhouse - ATS', description: 'Applicant Tracking System managing candidate pipelines, interview scheduling, and offer management. Feeds new hire data to Workday.' },
'30ft': { title: 'Greenhouse - Recruiting Platform', description: 'End-to-end ATS handling job postings, candidate sourcing, interview coordination, and offer letters. API integration with Workday automatically creates employee records on offer acceptance. Structured hiring workflows with customizable scorecards and approval chains.' }
},
google: {
'30000ft': { title: 'Google Workspace', description: 'Cloud productivity and collaboration suite.' },
'3000ft': { title: 'Google Workspace', description: 'Integrated suite including Gmail, Drive, Calendar, Meet. Provisioned automatically from Workday for new employees.' },
'30ft': { title: 'Google Workspace - Collaboration Platform', description: 'Complete productivity suite with Gmail (email), Drive (cloud storage), Calendar (scheduling), Meet (video conferencing), Docs/Sheets/Slides. Provisioned via Google Workspace Admin SDK using service accounts. User lifecycle managed through directory sync from Workday. Supports org units for department-based access control.' }
},
slack: {
'30000ft': { title: 'Slack - Messaging', description: 'Team communication and collaboration platform.' },
'3000ft': { title: 'Slack - Team Communication', description: 'Real-time messaging platform with channels, direct messages, and integrations. Users provisioned from Okta via SCIM.' },
'30ft': { title: 'Slack - Enterprise Collaboration', description: 'Cloud-based communication platform with public/private channels, threads, and 2000+ app integrations. SCIM 2.0 provisioning from Okta manages user accounts and channel memberships. Enterprise Grid supports multiple workspaces with centralized administration. Slack Connect enables external collaboration.' }
},
jamf: {
'30000ft': { title: 'Jamf - Mac Management', description: 'Apple device management platform.' },
'3000ft': { title: 'Jamf Pro - MDM', description: 'Mobile Device Management for macOS and iOS devices. Deploys software, enforces policies, and manages security settings.' },
'30ft': { title: 'Jamf Pro - Apple Enterprise Management', description: 'Comprehensive MDM for Apple ecosystem. Automated device enrollment via DEP/ADE. Policy-based configuration management using configuration profiles. Software distribution via Self Service portal and AutoPkg integration. Zero-touch deployment with prestage enrollments. API-driven automation for device lifecycle events.' }
},
ninjaone: {
'30000ft': { title: 'NinjaOne - Windows Management', description: 'Endpoint management for Windows devices.' },
'3000ft': { title: 'NinjaOne - RMM', description: 'Remote Monitoring and Management platform for Windows endpoints. Handles software deployment, patching, and monitoring.' },
'30ft': { title: 'NinjaOne - Unified Endpoint Management', description: 'Cloud-native RMM supporting Windows, Mac, and Linux. Automated patch management with approval workflows. Software deployment via Chocolatey integration. Real-time monitoring with customizable alerts. Remote control and scripting capabilities. Agent-based architecture with low system overhead.' }
},
acl: {
'30000ft': { title: 'ACL/TMM - Access Control', description: 'Custom role and access management system.' },
'3000ft': { title: 'ACL/TMM - Role Management', description: 'Internal application for requesting and approving role-based access to company applications. Integrates with Okta API for provisioning.' },
'30ft': { title: 'ACL/TMM - Access Control Layer', description: 'Custom-built identity governance platform managing role-based access control (RBAC). Workflow engine for access requests with multi-level approvals. Integrates with Okta Admin API to assign/revoke app access and group memberships. Audit logging for compliance. Scheduled access reviews and recertification campaigns.' }
},
autopkg: {
'30000ft': { title: 'AutoPkg - Mac Software', description: 'Automated software packaging for macOS.' },
'3000ft': { title: 'AutoPkg - Package Automation', description: 'Open-source tool that automates downloading and packaging Mac software for deployment via Jamf.' },
'30ft': { title: 'AutoPkg - macOS Software Automation', description: 'Python-based automation framework for Mac software lifecycle. Recipe-driven downloads from vendor sites with checksum verification. Automatic package creation and upload to Jamf Pro distribution points. Version tracking and update notifications. Custom recipes for internal apps. Integrated with Git for recipe management.' }
},
chocolatey: {
'30000ft': { title: 'Chocolatey - Windows Software', description: 'Package manager for Windows applications.' },
'3000ft': { title: 'Chocolatey - Package Manager', description: 'Windows package management system similar to apt/yum. NinjaOne deploys software using Chocolatey packages.' },
'30ft': { title: 'Chocolatey - Windows Package Management', description: 'PowerShell-based package manager with 9000+ community packages. Silent installation with dependency resolution. Version pinning and rollback capabilities. NuGet package format for internal apps. Integrates with NinjaOne for automated deployment. Supports both online and offline installation scenarios.' }
},
'workspace-one': {
'30000ft': { title: 'Workspace ONE - Mobile MDM', description: 'Mobile device management platform.' },
'3000ft': { title: 'Workspace ONE - UEM', description: 'Unified Endpoint Management for iOS and Android mobile devices. Manages corporate and BYOD devices.' },
'30ft': { title: 'VMware Workspace ONE - Mobile UEM', description: 'Enterprise mobility management platform for iOS/Android. Apple DEP and Android Enterprise integration for zero-touch enrollment. App deployment via managed app store. Conditional access policies with compliance checking. Email/calendar configuration via Exchange ActiveSync. Remote wipe and device security enforcement.' }
},
'aws-lambda': {
'30000ft': { title: 'AWS Lambda - Automation', description: 'Serverless compute functions.' },
'3000ft': { title: 'AWS Lambda - Functions', description: 'Serverless compute platform running automation code triggered by Okta webhooks and other events.' },
'30ft': { title: 'AWS Lambda - Event-Driven Compute', description: 'Serverless functions (Node.js/Python) processing Okta lifecycle webhooks. Triggered via API Gateway with request validation. Environment variables for secrets management. CloudWatch logging and X-Ray tracing. IAM roles for secure API access to Jamf, Slack, etc. VPC integration for private resource access. Handles user provisioning, deprovisioning, and attribute sync.' }
},
'aws-gateway': {
'30000ft': { title: 'API Gateway - Routing', description: 'API endpoint management.' },
'3000ft': { title: 'API Gateway - Webhook Receiver', description: 'Receives webhooks from Okta and routes them to Lambda functions for processing.' },
'30ft': { title: 'AWS API Gateway - HTTP API', description: 'RESTful API endpoints for Okta webhook consumption. Request validation with JSON schema. API key authentication and request throttling. CORS configuration for cross-origin requests. CloudWatch metrics and access logging. Routes to Lambda with payload transformation. Custom domain with TLS certificates.' }
},
zapier: {
'30000ft': { title: 'Zapier - Automation', description: 'No-code workflow automation platform.' },
'3000ft': { title: 'Zapier - Integration Platform', description: 'Connects apps and automates workflows without code. Creates integrations between various IT tools.' },
'30ft': { title: 'Zapier - iPaaS (Integration Platform as a Service)', description: 'No-code automation with 5000+ app integrations. Multi-step Zaps with conditional logic and data transformation. Webhooks for custom triggers and actions. Error handling and retry logic. Used for lightweight integrations not requiring custom Lambda code. Connects HR systems to productivity tools.' }
},
datadog: {
'30000ft': { title: 'Datadog - Monitoring', description: 'Infrastructure and application monitoring.' },
'3000ft': { title: 'Datadog - Observability Platform', description: 'Monitors infrastructure, applications, and logs. Provides metrics, traces, and alerts for all IT systems.' },
'30ft': { title: 'Datadog - Full-Stack Observability', description: 'Cloud-scale monitoring with metrics, APM, logs, and distributed tracing. Agent-based collection from all infrastructure. Custom dashboards and anomaly detection. Integration with PagerDuty for alerting. Monitors Lambda functions, EC2 instances, and SaaS applications. Synthetic monitoring for uptime checks. Log aggregation with pattern analysis.' }
},
pagerduty: {
'30000ft': { title: 'PagerDuty - Alerting', description: 'Incident response and on-call management.' },
'3000ft': { title: 'PagerDuty - Incident Management', description: 'Alert aggregation and on-call scheduling. Routes critical alerts to the right team members.' },
'30ft': { title: 'PagerDuty - Digital Operations Management', description: 'Incident response platform with intelligent alert routing. Escalation policies and on-call schedules. Integration with Datadog, CloudWatch, and other monitoring tools. Event intelligence for noise reduction and deduplication. Incident workflows with status pages. Postmortem templates and analytics. Mobile app for 24/7 response.' }
},
jira: {
'30000ft': { title: 'Jira - Project Management', description: 'Issue tracking and project management.' },
'3000ft': { title: 'Jira - Ticketing System', description: 'Agile project management and issue tracking. Used for IT tickets, software development, and project planning.' },
'30ft': { title: 'Atlassian Jira - Work Management Platform', description: 'Comprehensive issue tracking with customizable workflows. Scrum/Kanban boards for agile teams. Service desk for IT support tickets. Automation rules for ticket routing and SLA management. JQL (Jira Query Language) for advanced searches. REST API for integrations. Custom fields and issue types for different teams.' }
},
confluence: {
'30000ft': { title: 'Confluence - Documentation', description: 'Team wiki and knowledge management.' },
'3000ft': { title: 'Confluence - Knowledge Base', description: 'Collaborative documentation platform. Stores team wikis, runbooks, and technical documentation.' },
'30ft': { title: 'Atlassian Confluence - Team Workspace', description: 'Enterprise wiki with rich text editing and page hierarchy. Spaces for team/project organization. Macros for dynamic content and integrations. Page restrictions and approval workflows. Version history and page watching. Integrates with Jira for requirements tracking. Search across all content with page analytics.' }
},
github: {
'30000ft': { title: 'GitHub - Code Repository', description: 'Version control and code collaboration.' },
'3000ft': { title: 'GitHub - Source Control', description: 'Git repository hosting with code review and CI/CD. Stores all company source code and infrastructure-as-code.' },
'30ft': { title: 'GitHub Enterprise - DevOps Platform', description: 'Self-hosted Git repositories with branch protection rules. Pull request reviews with CODEOWNERS. GitHub Actions for CI/CD pipelines. Dependabot for dependency updates. Secret scanning and security advisories. Organizations and teams for access control. Integrates with Jira for issue tracking. Terraform/IaC stored as code.' }
},
'1password': {
'30000ft': { title: '1Password - Secrets', description: 'Password and secrets management.' },
'3000ft': { title: '1Password - Password Manager', description: 'Enterprise password manager for credentials and secrets. Vaults organized by team with granular permissions.' },
'30ft': { title: '1Password - Secrets Management', description: 'Enterprise password vault with AES-256 encryption. Vaults for teams with item-level permissions. CLI and browser extensions for developer workflows. Integrations with Okta for SSO. Audit logs for compliance. Secrets automation for CI/CD pipelines. Emergency access and recovery keys. SCIM provisioning for user lifecycle.' }
},
'lyft-infra': {
'30000ft': { title: 'Lyft Production - Infrastructure', description: 'Production application infrastructure.' },
'3000ft': { title: 'Lyft Production Infrastructure', description: 'AWS-based production environment running Lyft services. Managed via Terraform and automated deployments.' },
'30ft': { title: 'Lyft Production Infrastructure', description: 'Multi-region AWS infrastructure with ECS/EKS for container orchestration. Auto-scaling groups with load balancers. RDS databases with read replicas. CloudFront CDN for static assets. Terraform for infrastructure-as-code. CI/CD via GitHub Actions. Monitoring with Datadog and CloudWatch. Incident response via PagerDuty.' }
}
};
const getNodeInfo = (nodeId) => {
const info = nodeInfo[nodeId];
if (!info) return null;
return info[zoomLevel] || info['30000ft'];
};
useEffect(() => {
const updateCanvasSize = () => {
if (svgRef.current) {
const rect = svgRef.current.getBoundingClientRect();
setCanvasSize({ width: rect.width, height: rect.height });
}
};
updateCanvasSize();
window.addEventListener('resize', updateCanvasSize);
return () => window.removeEventListener('resize', updateCanvasSize);
}, []);
const getSpacing = () => {
const baseWidth = 1800;
const baseHeight = 800;
const scaleX = canvasSize.width / baseWidth;
const scaleY = canvasSize.height / baseHeight;
const scale = Math.min(scaleX, scaleY, 1.2);
return {
horizontal: 200 * scale,
vertical: 150 * scale,
};
};
const spacing = getSpacing();
const cols = {
hris: spacing.horizontal * 1,
idp: spacing.horizontal * 2,
collab: spacing.horizontal * 3,
mdm: spacing.horizontal * 4,
automation: spacing.horizontal * 5,
packageMgmt: spacing.horizontal * 6,
monitoring: spacing.horizontal * 7,
productivity: spacing.horizontal * 8
};
const snapToGrid = (value) => Math.round(value / 20) * 20;
const nodes30000ft = [
{ id: 'greenhouse', x: cols.hris, y: spacing.vertical * 1.2, label: 'Greenhouse', logo: logos.greenhouse, scale: 0.9 },
{ id: 'workday', x: cols.hris, y: spacing.vertical * 2.2, label: 'Workday', logo: logos.workday },
{ id: 'acl', x: cols.idp, y: spacing.vertical * 1.2, label: 'ACL/TMM', logo: logos.onepassword, scale: 0.9 },
{ id: 'okta', x: cols.idp, y: spacing.vertical * 2.2, label: 'Okta', logo: logos.okta },
{ id: 'google', x: cols.collab, y: spacing.vertical * 1.2, label: 'Google', logo: logos.google },
{ id: 'slack', x: cols.collab, y: spacing.vertical * 2.2, label: 'Slack', logo: logos.slack },
{ id: 'jamf', x: cols.mdm, y: spacing.vertical * 1.2, label: 'Jamf', logo: logos.jamf },
{ id: 'ninjaone', x: cols.mdm, y: spacing.vertical * 2.2, label: 'NinjaOne', logo: logos.ninjaone },
{ id: 'workspace-one', x: cols.mdm, y: spacing.vertical * 3.2, label: 'Workspace ONE', logo: logos.vmware, scale: 0.9 },
];
const cats30000ft = [
{ x: cols.hris, y: spacing.vertical * 0.6, label: 'HRIS' },
{ x: cols.idp, y: spacing.vertical * 0.6, label: 'Identity' },
{ x: cols.collab, y: spacing.vertical * 0.6, label: 'Collaboration' },
{ x: cols.mdm, y: spacing.vertical * 0.6, label: 'MDM' },
];
const nodes3000ft = [
{ id: 'greenhouse', x: cols.hris, y: spacing.vertical * 1.2, label: 'Greenhouse', logo: logos.greenhouse, scale: 0.9 },
{ id: 'workday', x: cols.hris, y: spacing.vertical * 2.2, label: 'Workday', logo: logos.workday },
{ id: 'acl', x: cols.idp, y: spacing.vertical * 1.2, label: 'ACL/TMM', logo: logos.onepassword, scale: 0.9 },
{ id: 'okta', x: cols.idp, y: spacing.vertical * 2.2, label: 'Okta', logo: logos.okta },
{ id: 'google', x: cols.collab, y: spacing.vertical * 1.2, label: 'Google', logo: logos.google },
{ id: 'slack', x: cols.collab, y: spacing.vertical * 2.2, label: 'Slack', logo: logos.slack },
{ id: 'jamf', x: cols.mdm, y: spacing.vertical * 1.2, label: 'Jamf', logo: logos.jamf },
{ id: 'ninjaone', x: cols.mdm, y: spacing.vertical * 2.2, label: 'NinjaOne', logo: logos.ninjaone },
{ id: 'workspace-one', x: cols.mdm, y: spacing.vertical * 3.2, label: 'Workspace ONE', logo: logos.vmware, scale: 0.9 },
{ id: 'autopkg', x: cols.packageMgmt, y: spacing.vertical * 1.2, label: 'AutoPkg', logo: logos.autopkg, scale: 0.9 },
{ id: 'chocolatey', x: cols.packageMgmt, y: spacing.vertical * 2.2, label: 'Chocolatey', logo: logos.chocolatey, scale: 0.9 },
{ id: 'zapier', x: cols.automation, y: spacing.vertical * 1.2, label: 'Zapier', logo: logos.zapier, scale: 0.9 },
];
const cats3000ft = [
{ x: cols.hris, y: spacing.vertical * 0.6, label: 'HRIS' },
{ x: cols.idp, y: spacing.vertical * 0.6, label: 'Identity' },
{ x: cols.collab, y: spacing.vertical * 0.6, label: 'Collaboration' },
{ x: cols.mdm, y: spacing.vertical * 0.6, label: 'MDM' },
{ x: cols.packageMgmt, y: spacing.vertical * 0.6, label: 'Package Mgmt' },
{ x: cols.automation, y: spacing.vertical * 0.6, label: 'Automation' },
];
const nodes30ft = [
{ id: 'greenhouse', x: cols.hris, y: spacing.vertical * 1.2, label: 'Greenhouse', logo: logos.greenhouse, scale: 0.85 },
{ id: 'workday', x: cols.hris, y: spacing.vertical * 2.1, label: 'Workday', logo: logos.workday },
{ id: 'acl', x: cols.idp, y: spacing.vertical * 1.2, label: 'ACL/TMM', logo: logos.onepassword, scale: 0.85 },
{ id: 'okta', x: cols.idp, y: spacing.vertical * 2.1, label: 'Okta', logo: logos.okta },
{ id: '1password', x: cols.idp, y: spacing.vertical * 3, label: '1Password', logo: logos.onepassword, scale: 0.85 },
{ id: 'google', x: cols.collab, y: spacing.vertical * 1.2, label: 'Google', logo: logos.google },
{ id: 'gmail', x: cols.collab, y: spacing.vertical * 2, label: 'Gmail', logo: logos.gmail, scale: 0.8 },
{ id: 'gdrive', x: cols.collab, y: spacing.vertical * 2.7, label: 'Drive', logo: logos.drive, scale: 0.8 },
{ id: 'slack', x: cols.collab, y: spacing.vertical * 3.4, label: 'Slack', logo: logos.slack },
{ id: 'jamf', x: cols.mdm, y: spacing.vertical * 1.2, label: 'Jamf', logo: logos.jamf },
{ id: 'ninjaone', x: cols.mdm, y: spacing.vertical * 2.1, label: 'NinjaOne', logo: logos.ninjaone },
{ id: 'workspace-one', x: cols.mdm, y: spacing.vertical * 3, label: 'Workspace ONE', logo: logos.vmware, scale: 0.85 },
{ id: 'zapier', x: cols.automation, y: spacing.vertical * 1.2, label: 'Zapier', logo: logos.zapier, scale: 0.85 },
{ id: 'aws-gateway', x: cols.automation, y: spacing.vertical * 2, label: 'API Gateway', logo: logos.aws, scale: 0.85 },
{ id: 'aws-lambda', x: cols.automation, y: spacing.vertical * 2.8, label: 'Lambda', logo: logos.aws, scale: 0.85 },
{ id: 'lyft-infra', x: cols.automation, y: spacing.vertical * 3.6, label: 'Lyft Production', logo: logos.aws, scale: 0.85 },
{ id: 'autopkg', x: cols.packageMgmt, y: spacing.vertical * 1.2, label: 'AutoPkg', logo: logos.autopkg, scale: 0.85 },
{ id: 'chocolatey', x: cols.packageMgmt, y: spacing.vertical * 2.1, label: 'Chocolatey', logo: logos.chocolatey, scale: 0.85 },
{ id: 'datadog', x: cols.monitoring, y: spacing.vertical * 1.2, label: 'Datadog', logo: logos.datadog, scale: 0.85 },
{ id: 'pagerduty', x: cols.monitoring, y: spacing.vertical * 2.1, label: 'PagerDuty', logo: logos.pagerduty, scale: 0.85 },
{ id: 'jira', x: cols.productivity, y: spacing.vertical * 1.2, label: 'Jira', logo: logos.jira, scale: 0.85 },
{ id: 'confluence', x: cols.productivity, y: spacing.vertical * 2.1, label: 'Confluence', logo: logos.confluence, scale: 0.85 },
{ id: 'github', x: cols.productivity, y: spacing.vertical * 3, label: 'GitHub', logo: logos.github, scale: 0.85 },
];
const cats30ft = [
{ x: cols.hris, y: spacing.vertical * 0.6, label: 'HRIS' },
{ x: cols.idp, y: spacing.vertical * 0.6, label: 'Identity' },
{ x: cols.collab, y: spacing.vertical * 0.6, label: 'Collaboration' },
{ x: cols.mdm, y: spacing.vertical * 0.6, label: 'MDM' },
{ x: cols.automation, y: spacing.vertical * 0.6, label: 'Automation' },
{ x: cols.packageMgmt, y: spacing.vertical * 0.6, label: 'Package Mgmt' },
{ x: cols.monitoring, y: spacing.vertical * 0.6, label: 'Monitoring' },
{ x: cols.productivity, y: spacing.vertical * 0.6, label: 'Productivity' },
];
const getNodes = () => {
const baseNodes = zoomLevel === '30000ft' ? nodes30000ft : zoomLevel === '3000ft' ? nodes3000ft : nodes30ft;
const nodesWithPositions = baseNodes.map(node => {
const savedPos = nodePositions[`${zoomLevel}-${node.id}`];
return savedPos ? { ...node, x: savedPos.x, y: savedPos.y } : node;
});
if (activeFlow === 'newUser') {
const userPos = nodePositions[`${zoomLevel}-user`] || { x: spacing.horizontal * 0.8, y: spacing.vertical * 1.7 };
const flowNodes = ['user', 'greenhouse', 'workday', 'google', 'okta', 'slack', 'jamf', 'ninjaone'];
const flowLayout = {
user: { x: spacing.horizontal * 0.8, y: spacing.vertical * 1.7 },
greenhouse: { x: spacing.horizontal * 2, y: spacing.vertical * 1.7 },
workday: { x: spacing.horizontal * 3.2, y: spacing.vertical * 1.7 },
google: { x: spacing.horizontal * 4.8, y: spacing.vertical * 1.1 },
okta: { x: spacing.horizontal * 4.8, y: spacing.vertical * 2.4 },
slack: { x: spacing.horizontal * 6.2, y: spacing.vertical * 1.4 },
jamf: { x: spacing.horizontal * 6.2, y: spacing.vertical * 2.4 },
ninjaone: { x: spacing.horizontal * 6.2, y: spacing.vertical * 3.4 }
};
const activeNodes = [{ id: 'user', ...userPos, label: 'New Hire', logo: 'user', isUser: true }];
nodesWithPositions.forEach(node => {
if (flowNodes.includes(node.id)) {
const savedFlowPos = nodePositions[`${zoomLevel}-flow-newUser-${node.id}`];
const pos = savedFlowPos || flowLayout[node.id];
activeNodes.push({ ...node, x: pos.x, y: pos.y });
}
});
return activeNodes;
}
if (activeFlow === 'softwareDownload') {
const flowNodes = ['jamf', 'autopkg', 'ninjaone', 'chocolatey'];
const flowLayout = {
jamf: { x: spacing.horizontal * 2, y: spacing.vertical * 1.5 },
autopkg: { x: spacing.horizontal * 2, y: spacing.vertical * 2.8 },
ninjaone: { x: spacing.horizontal * 3.8, y: spacing.vertical * 1.5 },
chocolatey: { x: spacing.horizontal * 3.8, y: spacing.vertical * 2.8 }
};
const activeNodes = [];
nodesWithPositions.forEach(node => {
if (flowNodes.includes(node.id)) {
const savedFlowPos = nodePositions[`${zoomLevel}-flow-softwareDownload-${node.id}`];
const pos = savedFlowPos || flowLayout[node.id];
activeNodes.push({ ...node, x: pos.x, y: pos.y });
}
});
return activeNodes;
}
if (activeFlow === 'oktaAutomation') {
const flowNodes = ['okta', 'aws-gateway', 'aws-lambda', 'jamf'];
const flowLayout = {
okta: { x: spacing.horizontal * 1.2, y: spacing.vertical * 2 },
'aws-gateway': { x: spacing.horizontal * 2.5, y: spacing.vertical * 1.5 },
'aws-lambda': { x: spacing.horizontal * 2.5, y: spacing.vertical * 2.5 },
jamf: { x: spacing.horizontal * 3.8, y: spacing.vertical * 2 }
};
const activeNodes = [];
nodesWithPositions.forEach(node => {
if (flowNodes.includes(node.id)) {
const savedFlowPos = nodePositions[`${zoomLevel}-flow-oktaAutomation-${node.id}`];
const pos = savedFlowPos || flowLayout[node.id];
activeNodes.push({ ...node, x: pos.x, y: pos.y });
}
});
return activeNodes;
}
if (activeFlow === 'aclProvisioning') {
const userPos = nodePositions[`${zoomLevel}-user`] || { x: spacing.horizontal * 0.8, y: spacing.vertical * 2 };
const flowNodes = ['user', 'acl', 'okta', 'slack'];
const flowLayout = {
user: { x: spacing.horizontal * 0.8, y: spacing.vertical * 2 },
acl: { x: spacing.horizontal * 2.2, y: spacing.vertical * 2 },
okta: { x: spacing.horizontal * 3.6, y: spacing.vertical * 2 },
slack: { x: spacing.horizontal * 5, y: spacing.vertical * 2 }
};
const activeNodes = [{ id: 'user', ...userPos, label: 'User', logo: 'user', isUser: true }];
nodesWithPositions.forEach(node => {
if (flowNodes.includes(node.id)) {
const savedFlowPos = nodePositions[`${zoomLevel}-flow-aclProvisioning-${node.id}`];
const pos = savedFlowPos || flowLayout[node.id];
activeNodes.push({ ...node, x: pos.x, y: pos.y });
}
});
return activeNodes;
}
return nodesWithPositions;
};
const getCategories = () => {
if (activeFlow) return [];
if (zoomLevel === '30000ft') return cats30000ft;
if (zoomLevel === '3000ft') return cats3000ft;
return cats30ft;
};
const getNodeStepNumbers = () => {
const stepMap = {};
if (activeFlow === 'newUser') {
stepMap.user = 1;
stepMap.greenhouse = 2;
stepMap.workday = 3;
stepMap.google = 4;
stepMap.okta = 4;
stepMap.slack = 5;
stepMap.jamf = 5;
stepMap.ninjaone = 5;
} else if (activeFlow === 'softwareDownload') {
stepMap.jamf = 1;
stepMap.ninjaone = 1;
stepMap.autopkg = 2;
stepMap.chocolatey = 2;
} else if (activeFlow === 'oktaAutomation') {
stepMap.okta = 1;
stepMap['aws-gateway'] = 2;
stepMap['aws-lambda'] = 3;
stepMap.jamf = 4;
} else if (activeFlow === 'aclProvisioning') {
stepMap.user = 1;
stepMap.acl = 2;
stepMap.okta = 3;
stepMap.slack = 4;
}
return stepMap;
};
const getEdges = () => {
const edges = [];
const currentNodes = getNodes();
const getNodePos = (id) => currentNodes.find(n => n.id === id);
if (activeFlow === 'newUser') {
const user = getNodePos('user');
const gh = getNodePos('greenhouse');
const wd = getNodePos('workday');
const google = getNodePos('google');
const okta = getNodePos('okta');
const slack = getNodePos('slack');
const jamf = getNodePos('jamf');
const ninja = getNodePos('ninjaone');
if (user && gh) {
edges.push({
edgeId: 'newUser-user-gh',
x1: user.x, y1: user.y, x2: gh.x, y2: gh.y,
color: '#8b5cf6',
label: 'Offer Accepted',
sublabel: 'Start date set'
});
}
if (gh && wd) {
edges.push({
edgeId: 'newUser-gh-wd',
x1: gh.x, y1: gh.y, x2: wd.x, y2: wd.y,
color: '#10b981',
label: 'New Hire Data',
sublabel: 'Employee info'
});
}
if (wd && google) {
const midY = snapToGrid(Math.min(wd.y, google.y) - 80);
edges.push({
edgeId: 'newUser-wd-google',
x1: wd.x, y1: wd.y, x2: google.x, y2: google.y,
color: '#3b82f6',
label: 'Provision Account',
sublabel: 'Email, calendar, drive',
controlPoints: [{ x: snapToGrid(wd.x), y: midY }, { x: snapToGrid(google.x), y: midY }]
});
}
if (wd && okta) {
const midY = snapToGrid(Math.max(wd.y, okta.y) + 80);
edges.push({
edgeId: 'newUser-wd-okta',
x1: wd.x, y1: wd.y, x2: okta.x, y2: okta.y,
color: '#1e40af',
label: 'Create Identity',
sublabel: 'SSO profile',
controlPoints: [{ x: snapToGrid(wd.x), y: midY }, { x: snapToGrid(okta.x), y: midY }]
});
}
if (okta && slack) {
edges.push({
edgeId: 'newUser-okta-slack',
x1: okta.x, y1: okta.y, x2: slack.x, y2: slack.y,
color: '#9333ea',
label: 'SCIM Provision',
sublabel: 'User + channels',
controlPoints: [{ x: snapToGrid((okta.x + slack.x) / 2), y: snapToGrid(slack.y) }]
});
}
if (okta && jamf) {
edges.push({
edgeId: 'newUser-okta-jamf',
x1: okta.x, y1: okta.y, x2: jamf.x, y2: jamf.y,
color: '#16a34a',
label: 'Device Assignment',
sublabel: 'Mac + prestage',
controlPoints: [{ x: snapToGrid((okta.x + jamf.x) / 2), y: snapToGrid(jamf.y) }]
});
}
if (okta && ninja) {
edges.push({
edgeId: 'newUser-okta-ninja',
x1: okta.x, y1: okta.y, x2: ninja.x, y2: ninja.y,
color: '#dc2626',
label: 'Device Assignment',
sublabel: 'Windows + policy',
controlPoints: [{ x: snapToGrid((okta.x + ninja.x) / 2), y: snapToGrid(ninja.y) }]
});
}
}
if (activeFlow === 'softwareDownload' && zoomLevel !== '30000ft') {
const jamf = getNodePos('jamf');
const autopkg = getNodePos('autopkg');
const ninja = getNodePos('ninjaone');
const choco = getNodePos('chocolatey');
if (jamf && autopkg) {
edges.push({
edgeId: 'software-jamf-autopkg',
x1: jamf.x, y1: jamf.y, x2: autopkg.x, y2: autopkg.y,
color: '#16a34a',
label: 'Mac Software',
sublabel: 'PKG deployment'
});
}
if (ninja && choco) {
edges.push({
edgeId: 'software-ninja-choco',
x1: ninja.x, y1: ninja.y, x2: choco.x, y2: choco.y,
color: '#dc2626',
label: 'Windows Software',
sublabel: 'EXE/MSI install'
});
}
}
if (activeFlow === 'oktaAutomation' && zoomLevel === '30ft') {
const okta = getNodePos('okta');
const gw = getNodePos('aws-gateway');
const lambda = getNodePos('aws-lambda');
const jamf = getNodePos('jamf');
if (okta && gw) {
const midY = snapToGrid(Math.min(okta.y, gw.y) - 80);
edges.push({
edgeId: 'automation-okta-gw',
x1: okta.x, y1: okta.y, x2: gw.x, y2: gw.y,
color: '#1e40af',
label: 'Webhook Event',
sublabel: 'User lifecycle',
controlPoints: [{ x: snapToGrid(okta.x), y: midY }, { x: snapToGrid(gw.x), y: midY }]
});
}
if (gw && lambda) {
edges.push({
edgeId: 'automation-gw-lambda',
x1: gw.x, y1: gw.y, x2: lambda.x, y2: lambda.y,
color: '#f97316',
label: 'Trigger Function',
sublabel: 'Process event'
});
}
if (lambda && jamf) {
const midY = snapToGrid(Math.min(lambda.y, jamf.y) - 80);
edges.push({
edgeId: 'automation-lambda-jamf',
x1: lambda.x, y1: lambda.y, x2: jamf.x, y2: jamf.y,
color: '#16a34a',
label: 'API Call',
sublabel: 'Update device',
controlPoints: [{ x: snapToGrid(lambda.x), y: midY }, { x: snapToGrid(jamf.x), y: midY }]
});
}
}
if (activeFlow === 'aclProvisioning' && zoomLevel !== '30000ft') {
const user = getNodePos('user');
const acl = getNodePos('acl');
const okta = getNodePos('okta');
const slack = getNodePos('slack');
if (user && acl) {
edges.push({
edgeId: 'acl-user-acl',
x1: user.x, y1: user.y, x2: acl.x, y2: acl.y,
color: '#8b5cf6',
label: 'Request Role',
sublabel: 'Needs app access'
});
}
if (acl && okta) {
edges.push({
edgeId: 'acl-acl-okta',
x1: acl.x, y1: acl.y, x2: okta.x, y2: okta.y,
color: '#6366f1',
label: 'Role Approved',
sublabel: 'API: Add app access'
});
}
if (okta && slack) {
edges.push({
edgeId: 'acl-okta-slack',
x1: okta.x, y1: okta.y, x2: slack.x, y2: slack.y,
color: '#9333ea',
label: 'SCIM Provision',
sublabel: 'User + channels'
});
}
}
return edges;
};
const getEdgeLabels = () => {
const edges = getEdges();
return edges.map(edge => {
if (!edge.label) return null;
const points = [{ x: edge.x1, y: edge.y1 }, ...(edge.controlPoints || []), { x: edge.x2, y: edge.y2 }];
let totalLength = 0;
const segments = [];
for (let i = 0; i < points.length - 1; i++) {
const dx = points[i + 1].x - points[i].x;
const dy = points[i + 1].y - points[i].y;
const length = Math.sqrt(dx * dx + dy * dy);
segments.push({ start: points[i], end: points[i + 1], length });
totalLength += length;
}
let targetLength = totalLength / 2;
let accumulatedLength = 0;
let defaultX = edge.x1;
let defaultY = edge.y1;
for (const segment of segments) {
if (accumulatedLength + segment.length >= targetLength) {
const ratio = (targetLength - accumulatedLength) / segment.length;
defaultX = segment.start.x + (segment.end.x - segment.start.x) * ratio;
defaultY = segment.start.y + (segment.end.y - segment.start.y) * ratio;
break;
}
accumulatedLength += segment.length;
}
const savedPos = labelPositions[`${zoomLevel}-${edge.edgeId}`];
return {
id: edge.edgeId,
x: savedPos ? savedPos.x : defaultX,
y: savedPos ? savedPos.y : defaultY,
label: edge.label,
sublabel: edge.sublabel,
color: edge.color
};
}).filter(Boolean);
};
const getActiveNodeIds = () => {
if (!activeFlow) return [];
if (activeFlow === 'newUser') return ['user', 'greenhouse', 'workday', 'google', 'okta', 'slack', 'jamf', 'ninjaone'];
if (activeFlow === 'softwareDownload') return ['jamf', 'autopkg', 'ninjaone', 'chocolatey'];
if (activeFlow === 'oktaAutomation') return ['okta', 'aws-gateway', 'aws-lambda', 'jamf'];
if (activeFlow === 'aclProvisioning') return ['user', 'acl', 'okta', 'slack'];
return [];
};
const handleFlowToggle = (flow) => {
if (activeFlow === flow) {
setAnimatingNodes(true);
setVisibleEdges([]);
setTimeout(() => setActiveFlow(null), 50);
} else {
setAnimatingNodes(true);
setTimeout(() => setActiveFlow(flow), 50);
setVisibleEdges([]);
setTimeout(() => {
setAnimatingNodes(false);
const edgeSequences = {
newUser: ['newUser-user-gh', 'newUser-gh-wd', 'newUser-wd-google', 'newUser-wd-okta', 'newUser-okta-slack', 'newUser-okta-jamf', 'newUser-okta-ninja'],
softwareDownload: ['software-jamf-autopkg', 'software-ninja-choco'],
oktaAutomation: ['automation-okta-gw', 'automation-gw-lambda', 'automation-lambda-jamf'],
aclProvisioning: ['acl-user-acl', 'acl-acl-okta', 'acl-okta-slack']
};
const sequence = edgeSequences[flow] || [];
sequence.forEach((edgeId, index) => {
setTimeout(() => setVisibleEdges(prev => [...prev, edgeId]), index * 500);
});
}, 800);
}
};
const handleNodeDragStart = (nodeId) => (e) => {
e.stopPropagation();
setDraggingNode(nodeId);
};
const handleLabelDragStart = (labelId) => (e) => {
e.stopPropagation();
setDraggingLabel(labelId);
};
const handleMouseDown = (e) => {
if (e.button === 0 && !draggingNode && !draggingLabel) {
setIsDragging(true);
setDragStart({ x: e.clientX - pan.x, y: e.clientY - pan.y });
}
};
const handleMouseMove = (e) => {
if (isDragging && !draggingNode && !draggingLabel) {
setPan({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y });
} else if (draggingNode) {
const svg = svgRef.current;
const pt = svg.createSVGPoint();
pt.x = e.clientX;
pt.y = e.clientY;
const svgP = pt.matrixTransform(svg.getScreenCTM().inverse());
const newX = (svgP.x - pan.x) / zoom;
const newY = (svgP.y - pan.y) / zoom;
const key = activeFlow ? `${zoomLevel}-flow-${activeFlow}-${draggingNode}` : `${zoomLevel}-${draggingNode}`;
const newPositions = { ...nodePositions, [key]: { x: newX, y: newY } };
setNodePositions(newPositions);
} else if (draggingLabel) {
const svg = svgRef.current;
const pt = svg.createSVGPoint();
pt.x = e.clientX;
pt.y = e.clientY;
const svgP = pt.matrixTransform(svg.getScreenCTM().inverse());
const newX = (svgP.x - pan.x) / zoom;
const newY = (svgP.y - pan.y) / zoom;
const key = `${zoomLevel}-${draggingLabel}`;
const newPositions = { ...labelPositions, [key]: { x: newX, y: newY } };
setLabelPositions(newPositions);
}
};
const handleMouseUp = () => {
setIsDragging(false);
setDraggingNode(null);
setDraggingLabel(null);
};
const handleWheel = (e) => {
e.preventDefault();
const delta = e.deltaY > 0 ? 0.975 : 1.025;
const newZoom = Math.min(Math.max(0.3, zoom * delta), 3);
const svg = svgRef.current;
const rect = svg.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const pointBeforeZoomX = (mouseX - pan.x) / zoom;
const pointBeforeZoomY = (mouseY - pan.y) / zoom;
const pointAfterZoomX = (mouseX - pan.x) / newZoom;
const pointAfterZoomY = (mouseY - pan.y) / newZoom;
const newPanX = pan.x + (pointAfterZoomX - pointBeforeZoomX) * newZoom;
const newPanY = pan.y + (pointAfterZoomY - pointBeforeZoomY) * newZoom;
setZoom(newZoom);
setPan({ x: newPanX, y: newPanY });
};
const nodes = getNodes();
const edges = getEdges();
const edgeLabels = getEdgeLabels();
const categories = getCategories();
const activeNodeIds = getActiveNodeIds();
const nodeSteps = getNodeStepNumbers();
const displayEdges = activeFlow ? edges.filter(edge => visibleEdges.includes(edge.edgeId)) : edges;
const displayLabels = activeFlow ? edgeLabels.filter(label => visibleEdges.includes(label.id)) : edgeLabels;
return (
Zoom: {Math.round(zoom * 100)}%
💡 Drag canvas • Scroll to zoom • Drag nodes/labels
{activeFlow && (
{activeFlow === 'newUser' && '👤 New User Onboarding'}
{activeFlow === 'softwareDownload' && '📦 Software Distribution'}
{activeFlow === 'oktaAutomation' && 'âš¡ Okta Webhook Automation'}
{activeFlow === 'aclProvisioning' && '🔑 ACL Role Provisioning'}
{activeFlow === 'newUser' && 'New hire → Greenhouse → Workday → Google/Okta → Downstream apps'}
{activeFlow === 'softwareDownload' && 'Jamf/NinjaOne → AutoPkg/Chocolatey'}
{activeFlow === 'oktaAutomation' && 'Okta → API Gateway → Lambda → Jamf'}
{activeFlow === 'aclProvisioning' && 'User → ACL → Okta → Slack'}
)}
{selectedNode && getNodeInfo(selectedNode) && (
setSelectedNode(null)}>
e.stopPropagation()}>
{getNodeInfo(selectedNode).title}
{getNodeInfo(selectedNode).description}
💡 View changes with different zoom levels for more/less detail
)}
);
};
export default ITInfrastructureDiagram;